/**
 * Copyright 2019 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.fos.oss;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.model.Bucket;
import com.aliyun.oss.model.DeleteObjectsRequest;
import com.aliyun.oss.model.DeleteObjectsResult;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.ListObjectsRequest;
import com.aliyun.oss.model.ObjectMetadata;
import com.jianggujin.fos.JBucket;
import com.jianggujin.fos.JClient;
import com.jianggujin.fos.JClientWrapper;
import com.jianggujin.fos.JFOSException;
import com.jianggujin.fos.JListObjectsRequest;
import com.jianggujin.fos.JObject;
import com.jianggujin.fos.JObjectListing;
import com.jianggujin.fos.JObjectMetadata;
import com.jianggujin.fos.oss.JOSSConfiguration.JOSSClientConfiguration;
import com.jianggujin.fos.oss.JOSSConfiguration.JOSSSessionTokenConfiguration;
import com.jianggujin.fos.util.JObjectUtils;

import lombok.Getter;
import lombok.NonNull;

/**
 * 阿里云客户端
 * 
 * @author jianggujin
 *
 */
@Getter
public class JOSSClient implements JClient {
    private JOSSConfiguration configuration;
    private JClientWrapper<OSS> wrapper;

    public JOSSClient(@NonNull JOSSConfiguration configuration) {
        this.configuration = configuration;
        ClientBuilderConfiguration config = new ClientBuilderConfiguration();
        JOSSClientConfiguration clientConfiguration = configuration.getClientConfiguration();
        if (clientConfiguration != null) {
            JObjectUtils.copyProperties(configuration, config, true);
        }
        JOSSSessionTokenConfiguration sessionTokenConfiguration = configuration.getSessionTokenConfiguration();
        if (sessionTokenConfiguration != null) {
            String securityToken = sessionTokenConfiguration.getSecurityToken();
            // 已有securityToken，直接初始化
            if (securityToken != null) {
                this.wrapper = new JOSSClientWrapper(configuration.getEndPoint(), new DefaultCredentialProvider(
                        configuration.getAccessKeyId(), configuration.getSecretAccessKey(), securityToken), config);
                return;
            }
            // 自动获取sessionToken
            this.wrapper = new JOSSSecurityClientWrapper(configuration.getEndPoint(), config,
                    new JOSSSecurityTokenService(configuration));
            return;
        }

        this.wrapper = new JOSSClientWrapper(configuration.getEndPoint(),
                new DefaultCredentialProvider(configuration.getAccessKeyId(), configuration.getSecretAccessKey()),
                config);
    }

    @Override
    public List<JBucket> listBuckets() throws JFOSException {
        try {
            List<Bucket> buckets = this.wrapper.getClient().listBuckets();
            List<JBucket> jBuckets = new ArrayList<JBucket>(buckets.size());
            for (Bucket bucket : buckets) {
                jBuckets.add(new JOSSBucket(bucket));
            }
            return jBuckets;
        } catch (Exception e) {
            throw new JFOSException(e.getMessage(), e);
        }
    }

    @Override
    public JBucket createBucket(String bucketName) throws JFOSException {
        return new JOSSBucket(this.wrapper.getClient().createBucket(bucketName));
    }

    @Override
    public void deleteBucket(String bucketName) throws JFOSException {
        this.wrapper.getClient().deleteBucket(bucketName);
    }

    @Override
    public boolean doesBucketExist(String bucketName) throws JFOSException {
        return this.wrapper.getClient().doesBucketExist(bucketName);
    }

    @Override
    public JObjectListing listObjects(String bucketName) throws JFOSException {
        return new JOSSObjectListing(this.wrapper.getClient().listObjects(
                new ListObjectsRequest(bucketName, null, null, null, JListObjectsRequest.DEFAULT_RETURNED_KEYS_LIMIT)));
    }

    @Override
    public JObjectListing listObjects(String bucketName, String prefix) throws JFOSException {
        return new JOSSObjectListing(this.wrapper.getClient().listObjects(new ListObjectsRequest(bucketName, prefix,
                null, null, JListObjectsRequest.DEFAULT_RETURNED_KEYS_LIMIT)));
    }

    @Override
    public JObjectListing listObjects(JListObjectsRequest request) throws JFOSException {
        return new JOSSObjectListing(
                this.wrapper.getClient().listObjects(new ListObjectsRequest(request.getBucketName(),
                        request.getPrefix(), request.getMarker(), request.getDelimiter(), request.getMaxKeys())));
    }

    @Override
    public JObjectMetadata getObjectMetadata(String bucketName, String key) throws JFOSException {
        return new JOSSObjectMetadata(this.wrapper.getClient().getObjectMetadata(bucketName, key));
    }

    @Override
    public void deleteObject(String bucketName, String key) throws JFOSException {
        this.wrapper.getClient().deleteObject(bucketName, key);
    }

    @Override
    public List<String> deleteObjects(String bucketName, List<String> keys) throws JFOSException {
        DeleteObjectsResult deleteObjectsResult = this.wrapper.getClient()
                .deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(keys).withQuiet(true));
        return deleteObjectsResult.getDeletedObjects();
    }

    @Override
    public void copyObject(String sourceBucketName, String sourceKey, String destinationBucketName,
            String destinationKey) throws JFOSException {
        this.wrapper.getClient().copyObject(sourceBucketName, sourceKey, destinationBucketName, destinationKey);
    }

    @Override
    public JObject getObject(String bucketName, String key) throws JFOSException {
        return new JOSSObject(this.wrapper.getClient().getObject(bucketName, key));
    }

    @Override
    public JObjectMetadata getObject(String bucketName, String key, File file) throws JFOSException {
        return new JOSSObjectMetadata(this.wrapper.getClient().getObject(new GetObjectRequest(bucketName, key), file));
    }

    @Override
    public void putObject(String bucketName, String key, InputStream input) throws JFOSException {
        this.wrapper.getClient().putObject(bucketName, key, input);
    }

    @Override
    public void putObject(String bucketName, String key, InputStream input, String contentType) throws JFOSException {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        if (contentType != null) {
            objectMetadata.setContentType(contentType);
        }
        this.wrapper.getClient().putObject(bucketName, key, input, objectMetadata);
    }

    @Override
    public void putObject(String bucketName, String key, File file) throws JFOSException {
        this.wrapper.getClient().putObject(bucketName, key, file);
    }

    @Override
    public void putObject(String bucketName, String key, File file, String contentType) throws JFOSException {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        if (contentType != null) {
            objectMetadata.setContentType(contentType);
        }
        this.wrapper.getClient().putObject(bucketName, key, file, objectMetadata);
    }

    @Override
    public String generateUrl(String bucketName, String key, int expires) throws JFOSException {
        Date expiration = new Date(System.currentTimeMillis() + expires * 1000);
        // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
        return this.wrapper.getClient().generatePresignedUrl(bucketName, key, expiration).toString();
    }

    @Override
    public void destory() {
        if (this.wrapper != null) {
            this.wrapper.destory();
        }
    }
}
